// import mproj from 'mproj'
const mproj = {}
import utils from '../utils'
import {Bounds} from '../utils'
import {Matrix2D} from './index.js'

function MixedProjection(mainParams, options) {
  const mainFrame = initFrame(mainParams)
  const mainP = mainFrame.crs
  const frames = [mainFrame]
  const mixedP = initMixedProjection(mproj)

  mainP.__mixed_crs = mixedP

  mainP.addFrame = function(paramsArg) {
    const params = getFrameParams(paramsArg, options) // apply defaults and overrides
    const frame = initFrame(params)
    const m = new Matrix2D()
    //  originXY: the projected coordinates of the frame origin
    const originXY = params.origin ? projectFrameOrigin(params.origin, frame.crs) : [0, 0]
    const placementXY = params.placement || [0, 0]
    const dx = placementXY[0] - originXY[0] + (+params.dx || 0)
    const dy = placementXY[1] - originXY[1] + (+params.dy || 0)

    if (params.rotation) {
      m.rotate(params.rotation * Math.PI / 180.0, originXY[0], originXY[1])
    }
    if (params.scale) {
      m.scale(params.scale, params.scale, originXY[0], originXY[1])
    }
    m.translate(dx, dy)

    frame.matrix = m
    frames.push(frame)
    return this
  }
  function initFrame(params) {
    return {
      bounds: new Bounds(bboxToRadians(params.bbox)),
      crs:  mproj.pj_init(params.proj)
    }
  }
  function bboxToRadians(bbox) {
    const D2R = Math.PI / 180
    return bbox.map((deg) => {
      return deg * D2R
    })
  }
  function projectFrameOrigin(origin, P) {
    const xy = mproj.pj_fwd_deg({lam: origin[0], phi: origin[1]}, P)
    return [xy.x, xy.y]
  }
  mixedP.fwd = function(lp, xy) {
    let frame, xy2
    for (let i=0, n=frames.length; i<n; i++) {
      frame = frames[i]
      if (frame.bounds.containsPoint(lp.lam, lp.phi)) {
        xy2 = mproj.pj_fwd(lp, frame.crs)
        if (frame.matrix) {
          frame.matrix.transformXY(xy2.x, xy2.y, xy2)
        }
        break
      }
    }
    xy.x = xy2 ? xy2.x : Infinity
    xy.y = xy2 ? xy2.y : Infinity
  }

  return mainP
}

function initMixedProjection(mproj) {
  if (!mproj.internal.pj_list.mixed) {
    mproj.pj_add((P) => {
      P.a = 1
    }, 'mixed', 'Mapshaper Mixed Projection')
  }
  return mproj.pj_init('+proj=mixed')
}
function getFrameParams(params, options) {
  const opts = options[params.name]
  utils.defaults(params, {scale: 1, dx: 0, dy: 0, rotation: 0}) // add defaults
  if (!opts) return params
  Object.keys(opts).forEach((key) => {
    const val = opts[key]
    if (key in params) {
      params[key] = opts[key]
    } else {
      params.proj = replaceProjParam(params.proj, key, val)
    }
  })
  return params
}
function replaceProjParam(proj, key, val) {
  const param = '+' + key + '='
  return proj.split(' ').map((str) => {
    if (str.indexOf(param) === 0) {
      str = str.substr(0, param.length) + val
    }
    return str
  }).join(' ')
}
export default MixedProjection
